package cn.itcast.nems.studentrecord.service.impl.studentrecord;

import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.nems.manager.payment.PaymentProperties;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.studentrecord.api.StudentRecordApi;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecordPayment;
import cn.itcast.nems.studentrecord.dao.mapper.StudentRecordPaymentMapper;
import cn.itcast.nems.studentrecord.dto.StudentRecordDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordCurrentPaymentDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordPaymentDTO;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordErrorBodyEnum;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordPaymentStateEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordPaymentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 学籍支付service实现类
 *
 * @author ChangLu.Gao
 * @version v1.0
 * @program nems-server
 * @description
 * @date 2023/11/17 17:13
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class StudentRecordPaymentServiceImpl extends ServiceImpl<StudentRecordPaymentMapper, StudentRecordPayment>
        implements StudentRecordPaymentService {

    private final PaymentProperties paymentProperties;

    private final StudentRecordApi studentRecordApi;

    //分母
    private final static BigDecimal DENOMINATOR = new BigDecimal("100");

    @Override
    public StudentRecordCurrentPaymentDTO queryPartialPaymentByStudentRecordId(@NotNull String studentRecordId) {
        StudentRecordDTO studentRecordDTO = this.studentRecordApi.queryById(studentRecordId);
        List<StudentRecordPayment> paidRecords = super.baseMapper.queryByStudentRecordId(studentRecordId, List.of(StudentRecordPaymentStateEnum.PAID.name()));
        return this.polymerizationPartialPaymentResult(studentRecordDTO, paidRecords);
    }



    /**
     * 聚合分次支付需要结果
     *
     * @param studentRecordDTO 学籍详情
     * @return 分次支付信息
     */
    private StudentRecordCurrentPaymentDTO polymerizationPartialPaymentResult(@NonNull StudentRecordDTO studentRecordDTO,
                                                                              @NonNull List<StudentRecordPayment> paidRecords) {
        StudentRecordCurrentPaymentDTO result = new StudentRecordCurrentPaymentDTO();
        result.setPaidAmount(BigDecimal.valueOf(studentRecordDTO.getPaidAmount()))
                .setActualAmountDue(studentRecordDTO.getActualAmountDue())
                // 待支付金额
                .setUnPaidAmount(BigDecimal.valueOf(studentRecordDTO.getPayableBalance()))
                .setLimitMinPaidAmount(paymentProperties.getPartial().getLimitMinPaidAmount())
                .setRemainPaymentTimes(paymentProperties.getPartial().getTotalPaymentTimes() - paidRecords.size())
                .setTotalPaymentTimes(paymentProperties.getPartial().getTotalPaymentTimes())
                .setPayUrl(paymentProperties.getStudentPayLinkDomain() + "/" + studentRecordDTO.getUniqueCode())
                .setStudentName(studentRecordDTO.getName())
                .setClazzName(studentRecordDTO.getClazzName())
        ;
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudentRecordCurrentPaymentDTO modifyPartialPaymentAmount(@NotNull String studentRecordId, @NotNull BigDecimal amount) {
        StudentRecordCurrentPaymentDTO result = this.queryPartialPaymentByStudentRecordId(studentRecordId);
        if (result.getRemainPaymentTimes() > 1) {
            Assert.isTrue(amount.compareTo(paymentProperties.getPartial().getLimitMinPaidAmount()) >= 0, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.LIMIT_PART_PAYMENT_AMOUNT, "小于最低限制"));
        } else {
            Assert.isTrue(amount.compareTo(result.getUnPaidAmount()) < 0, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.LIMIT_PART_PAYMENT_AMOUNT, "小于剩余应缴金额"));
        }
        Assert.isTrue(result.getRemainPaymentTimes() > 0, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.LIMIT_PART_PAYMENT_COUNT));
        List<StudentRecordPayment> pendingRecords = super.baseMapper.queryByStudentRecordId(studentRecordId, List.of(StudentRecordPaymentStateEnum.PENDING.name()));
        if (CollectionUtils.isNotEmpty(pendingRecords)) {
            if (pendingRecords.get(0).getAmount().equals(amount.intValue())) {
                return result;
            }
            if (StringUtils.isBlank(pendingRecords.get(0).getPaymentUrl())) {
                pendingRecords.get(0).setAmount(amount.multiply(DENOMINATOR).intValue());
                super.baseMapper.updateById(pendingRecords.get(0));
                return result;
            }
        }
        this.createNewPendingRecord(studentRecordId, amount);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOtherPendingRecordByStudentRecordId(@NonNull String studentRecordId) {
        int count = super.baseMapper.cancelOtherPendingRecordByStudentRecordId(studentRecordId);
        log.info("根据学籍ID:{}, 取消了{}条pending状态的记录", studentRecordId, count);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudentRecordPaymentDTO createNewPendingRecord(@NotNull String studentRecordId, @NotNull BigDecimal amount) {
        this.cancelOtherPendingRecordByStudentRecordId(studentRecordId);
        StudentRecordPayment newRecord = new StudentRecordPayment();
        newRecord.setStudentRecordId(studentRecordId);
        newRecord.setAmount(amount.multiply(DENOMINATOR).intValue());
        newRecord.setState(StudentRecordPaymentStateEnum.PENDING.name());
        newRecord.setCreatedBy("system");
        newRecord.setCreatedDatetime(LocalDateTime.now());
        super.baseMapper.insert(newRecord);
        return CustomBeanUtil.copyProperties(newRecord, new StudentRecordPaymentDTO());
    }

    @Override
    public List<StudentRecordPaymentDTO> queryByStudentRecordIdAndStatus(@NotNull String studentRecordId, StudentRecordPaymentStateEnum state) {
        var records = super.baseMapper.queryByStudentRecordId(studentRecordId,
                null == state ? null : List.of(state.name()));
        if (CollectionUtils.isEmpty(records)) {
            return null;
        }
        return records.stream()
                .map(o -> {
                    StudentRecordPaymentDTO s = CustomBeanUtil.copyProperties(o, new StudentRecordPaymentDTO());
                    s.setState(StudentRecordPaymentStateEnum.valueOf(o.getState()));
                    return s;
                })
                .toList();
    }

    @Override
    public @NotNull
    StudentRecordPaymentDTO queryByMerchantOrderNo(@NotNull String merchantOrderNo) {
        Assert.isTrue(StringUtils.isNotEmpty(merchantOrderNo), BizExceptionProducer.produce(StudentRecordErrorBodyEnum.PARAMS_ERROR));
        var record = super.baseMapper.queryByMerchantOrderNo(merchantOrderNo);
        Assert.notNull(record, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.PAYMENT_RECORD_NOT_EXISTS));
        StudentRecordPaymentDTO result = CustomBeanUtil.copyProperties(record, new StudentRecordPaymentDTO());
        result.setState(StudentRecordPaymentStateEnum.valueOf(record.getState()));
        return result;
    }
}
